package cachex

import (
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/datax/convertx"
	"gitee.com/zhongguo168a/gocodes/datax/schemax"
	"gitee.com/zhongguo168a/gocodes/datax/schemax/basickind"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"strconv"
	"strings"
)

// 设置字段的值，val会根据字段类型进行处理
func (s *Object) RefSet(field string, val interface{}) error {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.refSet(s.data, field, val)
}

// 设置字段的值，如果字段的类型是Object，使用此方法
func (s *Object) RefSetObject(field string, r IObject) {
	s.mutex.Lock()
	s.data[field] = r
	s.mutex.Unlock()
}

func (s *Object) RefSetNil() IObject {
	s.mutex.Lock()
	s.refSetNil()
	s.mutex.Unlock()
	return s
}

func (s *Object) refSetNil() IObject {
	s.data = nil
	return s
}
func (s *Object) RefSetAnyType(val string) {
	s.anyType = val
}

func (s *Object) RefSetByPath(path string, val interface{}) {
	if string(path[0]) != "/" {
		panic("路径的首字符必须为/")
	}
	path = path[1:]
	pathArr := strings.Split(path, "/")
	field := pathArr[len(pathArr)-1]
	pathArr = pathArr[:len(pathArr)-1]
	obj := s.getObjectByPath(pathArr)
	if obj != nil {
		obj.RefSet(field, val)
	}
}

func (s *Object) refSet(sdata datax.M, field string, fieldVal interface{}) error {
	switch styp := s.schemaType.(type) {
	case *schemax.MapType:
		switch vtyp := styp.Value.(type) {
		case *schemax.AnyType:
			obj, err := s.createSchemaAny(fieldVal, s.anyType)
			if err != nil {
				return errorx.Wrap(err, "createSchemaAny", datax.M{field: field})
			}
			sdata[field] = obj
		case *schemax.ClassType:
			sdata[field] = s.creaetSchemaClass(vtyp, fieldVal)
		default:
			sdata[field] = fieldVal
		}
	case *schemax.ArrayType:
		switch etyp := styp.Elem.(type) {
		case *schemax.AnyType:
			obj, err := s.createSchemaAny(fieldVal, s.anyType)
			if err != nil {
				return errorx.Wrap(err, "createSchemaAny", datax.M{field: field})
			}
			sdata[field] = obj
		case *schemax.ClassType:
			sdata[field] = s.creaetSchemaClass(etyp, fieldVal)
		default:
			sdata[field] = fieldVal
		}
	case *schemax.ClassType:
		declField := s.schemaDecl.GetField(field)
		if declField == nil {
			return errorx.New("field not found", datax.M{"field": field})
		}
		switch fieldTyp := declField.Type.(type) {
		case *schemax.AnyType:
			obj, err := s.createSchemaAny(fieldVal, declField.Tags["_type"])
			if err != nil {
				return errorx.Wrap(err, "createSchemaAny", datax.M{field: field})
			}
			sdata[field] = obj
		case *schemax.ClassType:
			obj, isObject := fieldVal.(IObject)
			if isObject {
			} else {
				obj = s.creaetSchemaClass(fieldTyp, fieldVal)
			}
			sdata[field] = obj
		case *schemax.MapType:
			var obj IObject
			if fieldVal == nil {
				obj = newObjectByType(fieldTyp)
			} else {
				obj = newObjectByType(fieldTyp)
			}
			obj.RefSetAnyType(declField.Tags["_type"])
			if fieldVal != nil {
				obj.refUpdate(fieldVal.(datax.M))
			} else {
				obj.refUpdate(nil)
			}

			sdata[field] = obj
		case *schemax.ArrayType:
			var obj IObject
			if fieldVal == nil {
				obj = NewObjectByType(fieldTyp, nil)
			} else {

				switch fval := fieldVal.(type) {
				case datax.M:
					obj = newObjectByType(fieldTyp)
					obj.RefSetAnyType(declField.Tags["_type"])
					obj.refUpdate(fval)
				case datax.A:
					vmap := datax.M{}
					for i, val := range fval {
						vmap[strconv.Itoa(i)] = val
					}
					obj = newObjectByType(fieldTyp)
					obj.RefSetAnyType(declField.Tags["_type"])
					obj.refUpdate(vmap)
				}
			}

			sdata[field] = obj
		case *schemax.BasicType:
			switch fieldTyp.Kind {
			case basickind.Float64:
				fallthrough
			case basickind.Float32:
				sdata[field] = convertx.AnyToFloat64(fieldVal)
			case basickind.Int8:
				fallthrough
			case basickind.Int16:
				fallthrough
			case basickind.Int32:
				fallthrough
			case basickind.Int64:
				sdata[field] = convertx.AnyToInt(fieldVal)
			case basickind.Uint8:
				fallthrough
			case basickind.Uint16:
				fallthrough
			case basickind.Uint32:
				fallthrough
			case basickind.Uint64:
				sdata[field] = convertx.AnyToInt(fieldVal)
			case basickind.String:
				sdata[field] = convertx.AnyToString(fieldVal)
			case basickind.Bool:
				sdata[field] = convertx.AnyToBool(fieldVal)
			default:
				sdata[field] = fieldVal
			}
		default:
			sdata[field] = fieldVal
		}

	default:
	}

	return nil
}
